# @Time : 2020/12/16 15:44
# @Author : Fioman 
# @Phone : 13149920693
import os
import cv2 as cv
import imutils
import numpy as np
def image_show(name, image, time, show_state=False):
    if True:
        width = int(image.shape[1] / 5)
        height = int(image.shape[0] / 5            )
        show_image = cv.resize(image, (width, height))
        cv.namedWindow("{}".format(name), cv.WINDOW_AUTOSIZE)
        cv.imshow("{}".format(name), show_image)
        cv.waitKey(0)


def get_width_heigth(file_name):
    if "w_" not in file_name or "h_" not in file_name:
        return None, None
    w_index = file_name.index("w_")
    h_index = file_name.index("h_")
    width = int(file_name[w_index + 2:w_index + 6])
    height = int(file_name[h_index + 2:h_index + 6])
    return width, height

def get_center_by_contour(contour):
    """
    根据轮廓获取中心点坐标,注意返回值是中心坐标的int形式
    :param contour: 轮廓值集合
    :return: 一个元组(cxInt,cyInt)
    """
    return int(cv.minAreaRect(contour)[0][0]), int(cv.minAreaRect(contour)[0][1])

def get_rect_points_clockwise(rectPoints):
    """
    根据一个矩形的四个顶点的坐标,获取它们顺时针排序后的四个顶点坐标
    :param rectPoints: 一个无序的矩形框的四个顶点
    :return: 返回从左上角开始的顺时针的四个顶点的坐标
    """
    rectPointsSorted = sorted(rectPoints, key=np.sum)
    leftTop = rectPointsSorted[0]
    rightBottom = rectPointsSorted[-1]

    if rectPointsSorted[1][0] > rectPointsSorted[2][0]:
        rightTop = rectPointsSorted[1]
        leftBottom = rectPointsSorted[2]
    else:
        rightTop = rectPointsSorted[2]
        leftBottom = rectPointsSorted[1]

    return leftTop, rightTop, rightBottom, leftBottom



def get_total_board_edge_lines(image, width, height):
    """
    获取四刀都不切的时候的四条边线.
    :param image:
    :param width:
    :param height:
    :return:
    """
    thresUsed = np.mean(image[:150,:1100]) + 13
    filterAreaRate = 0.001
    T,board = cv.threshold(image,thresUsed,255,cv.THRESH_BINARY)
    cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)

    # 2. 然后将找到的所有的满足要求的区域,通过白线连接起来,形成整个物料框
    singleBoardArea = width * height
    largeContours = [cnt for cnt in cnts if cv.contourArea(cnt) > singleBoardArea * filterAreaRate]
    if len(largeContours) >= 2:
        centerPoints = [get_center_by_contour(contour) for contour in largeContours]
        for index, center in enumerate(centerPoints):
            anotherCenter = centerPoints[(index + 1) % len(centerPoints)]
            cv.line(board, center, anotherCenter, 255, 20)
        image_show("JointCenterLine", board, 0, True)

        # 3. 连线之后再找一遍
        cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
        cnts = imutils.grab_contours(cnts)
        cntFirstFinded = sorted(cnts, key=cv.contourArea, reverse=True)[0]
    else:
        cntFirstFinded = largeContours[0]

    # 4.这个轮廓找到之后,然后将将这个轮廓的四个角的影响去除掉.
    rectPoints = np.int0(cv.boxPoints(cv.minAreaRect(cntFirstFinded)))  # 第一次找到的轮廓
    leftTop, rightTop, rightBottom, leftBottom = get_rect_points_clockwise(rectPoints)

    # 5.去除掉找到的物料区域的四个角,再找一次.
    boardFinal = image.copy()
    rectWidth = int(width * 0.2)
    rectHeight = int(height * 0.3)
    extend = 20
    boardFinal[leftTop[1] - extend:leftTop[1] + rectHeight, leftTop[0] - extend:leftTop[0] + rectWidth] = 0
    boardFinal[rightTop[1] - extend:rightTop[1] + rectHeight, rightTop[0] - rectWidth:rightTop[0] + extend] = 0
    boardFinal[leftBottom[1] - extend - rectHeight:leftBottom[1], leftBottom[0] - extend:leftBottom[0] + rectWidth] = 0
    boardFinal[rightBottom[1] - rectHeight - extend:rightBottom[1], rightBottom[0] - rectWidth:rightBottom[0] + extend] = 0
    image_show("FinalBoardWithoutAngle", boardFinal, 0, True)

    T, board = cv.threshold(boardFinal, thresUsed, 255, cv.THRESH_BINARY)
    image_show("FinalBoardThres", board, 0, True)
    cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    largeContours = [cnt for cnt in cnts if cv.contourArea(cnt) > singleBoardArea * filterAreaRate]

    # 2> 如果largeContours的数量大于2,就要连线,然后重新找一遍
    if len(largeContours) >= 2:
        centerPoints = [get_center_by_contour(contour) for contour in largeContours]
        for index, center in enumerate(centerPoints):
            anotherCenter = centerPoints[(index + 1) % len(centerPoints)]
            cv.line(board, center, anotherCenter, 255, 20)
        image_show("JointedCenterLine", board, 0, True)

        # 3> 连线之后再找一遍
        cnts = cv.findContours(board.copy(), cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
        cnts = imutils.grab_contours(cnts)
        cntFinalFinded = sorted(cnts, key=cv.contourArea, reverse=True)[0]
    else:
        cntFinalFinded = largeContours[0]

    box = cv.minAreaRect(cntFinalFinded)
    (cx, cy), (w, h), angle = box
    if angle > 70:
        angle = 90 - angle
        box = (cx, cy), (h, w), angle
    elif angle < -70:
        angle = 90 + angle
        box = (cx, cy), (h, w), angle

    rectPoints = np.int0(cv.boxPoints(box))
    boxMask = np.zeros(image.shape, dtype=np.uint8)
    cv.drawContours(boxMask, [rectPoints], -1, 255, -1)
    imageBlackAround = cv.bitwise_and(image, boxMask)
    return imageBlackAround, box



if __name__ == '__main__':
    filePath = r"D:\shiyun_raw"

    calcArgs = {
        "2": [2, 5, 5, 20, 20, 100, 0.3],
        "3": [2, 5, 5, 30, 30, 120, 0.6],
        "4": [5, 17, 11, 8, 20, 20, 0.5],
        "5": [2, 5, 5, 8, 15, 100, 0.2],
    }

    totalBoardHeight = 1295
    MM2Pix = 2.708
    mtx = [[0, 1, 0], [1, 0, 0]]
    calcThresBig = 15  # 偏大阈值
    calcThresSmall = 10  # 偏小阈值
    remainEdge = [1, 1, 1, 1]  # 留边宽度
    jointNumber = "2"  # 几拼板
    topRemain, leftRemain, bottomRemain, rightRemain = [int(x * MM2Pix) for x in remainEdge]
    noCut = False if any(remainEdge) else True

    keepDir = filePath
    if not os.path.isdir(keepDir):
        keepDir, _ = os.path.split(filePath)

    # 保存成功结果的实际路径
    # 保存失败图片的实际路径
    # 成功的图就删除掉,另外保存起来,放到图库里.
    keepOkPath = os.path.join(keepDir, "testFinished")
    keepOkResPath = os.path.join(keepDir, "testRes")

    if not os.path.exists(keepOkPath):
        os.mkdir(keepOkPath)
    if not os.path.exists(keepOkResPath):
        os.mkdir(keepOkResPath)
    fileNames = []  # 保存要去识别的所有的板
    if os.path.isfile(filePath):
        filePath, fileName = os.path.split(filePath)
        fileNames.append(fileName)
    else:
        for root, dirs, files in os.walk(filePath):
            if root != filePath:  # 过滤掉不是要处理的图像的目录
                continue
            for file in files:
                fileNames.append(file)

    for index, fileName in enumerate(fileNames):
        filePathReal = os.path.join(filePath, fileName)
        widthTest, heightTest = get_width_heigth(fileName)
        if widthTest is None or heightTest is None:
            print("*" * 10 + " 第{} 张图 ({}) 图像名称错误,已经跳过".format(index + 1, fileName) + "*" * 10)
            continue
        print("*" * 10 + " 第{} 张图 ({})".format(index + 1, fileName) + "*" * 10)

        imgTest = cv.imread(filePathReal, cv.IMREAD_GRAYSCALE)
        totalBoardTest = None


        res,info,upBox,downBox,totalBox = get_boxes(imgTest,widthTest,heightTest,calcThresBig,MM2Pix)
        firstLine,secondLine,thirdLine,forthLine = get_total_board_edge_lines(imgTest,widthTest,heightTest)

